﻿using System;
using System.Collections.Generic;
using System.Threading;
using System.Windows.Forms;
using IBNet.DayTrade.Indicators;
using IBNet.UtilsDataStructuresAndExtensions;

namespace IBNet.DayTrade
{
   public partial class DayTradeMainForm : Form, IWrapDayTradeMainForm
   {
      const int NumberOfPriceBarsToStore = 5;
      const RealTimeBarSeconds _RealTimeBarSeconds = RealTimeBarSeconds.Five;

      const int StockDesiredPositionSizeInDollars = 12000;
      const int YMNumberOfContractsToTrade = 5;
      const int ESNumberOfContractsToTrade = 2;

      IBClient ibClient;
      IWrapDayTradeMainForm IBClientWrapper;
      
      public static DayTradeMainForm MainForm
      {
         get
         {
            if (mainForm == null)
            {
               mainForm = new DayTradeMainForm();
            }
            return mainForm;
         }
      }
      private static DayTradeMainForm mainForm;
      private DayTradeMainForm()
      {
         InitializeComponent();

         ibClient = new IBClient();
         ibClient.OpenOrderReceived += client_OpenOrderReceived;
         ibClient.OrderStatusReceived += client_OrderStatusReceived;
         ibClient.Event_RealTimeBarReceived += client_RealTimeBarReceived;
         ibClient.Event_CurrentServerTimeReceived.AttachEventHandler(client_CurrentServerTimeReceived);
         ibClient.Event_HistoricalPriceBarReceived.AttachEventHandler(client_HistoricalPriceBarReceived);
         ibClient.Event_ErrorOccurred.AttachEventHandler(client_ErrorOccurred);
         ibClient.Event_NextValidOrderIdReceived.AttachEventHandler(client_NextValidOrderIdReceived);
         ibClient.Event_ExecutionDetailsReceived.AttachEventHandler(client_ExecutionDetailsReceived);
         ibClient.Event_AccountValueUpdated.AttachEventHandler(client_AccountValueUpdated);
         ibClient.Event_PortfolioUpdated.AttachEventHandler(client_PortfolioUpdated);

         _symbolsWithOpenDaytradeOrders =
            new ListViewObservableThreadSafeHashSet<string>(
               listViewSymbolsWithOpenDayTradeOrders);
         
         _whenNearMarketCloseUnsubscribeFromRealTimeBarSubscriptions =
            new AlarmClock(12, 59, 30, delegate
            {
               IBClientWrapper.UnsubscribeFromAllRealTimeBars();
               textBoxMessages.WriteLine(
                  "Automatically unsubscribed from all real time bar subscriptions at " +
                  DateTime.Now.ToLongTimeString());
            });

         IBClientWrapper = this as IWrapDayTradeMainForm;
      }

      AlarmClock _whenNearMarketCloseUnsubscribeFromRealTimeBarSubscriptions;

      // IB treats as an error the act of submitting an order with an order Id of 5 
      // after submitting an order with an order Id of 6.
      readonly object _onlySubmitStrictlyIncreasingOrderIdsLock = new object();

      int _currentOrderId;
      int _currentRequestId;
      bool _areDisconnected;
      bool Connected { get { return ibClient.Connected; } }

      ThreadSafeHashSet<int> _openingOrderIds = new ThreadSafeHashSet<int>();
      ThreadSafeDictionary<int, OrderAndContract> _orderIdToOrderAndContract = new ThreadSafeDictionary<int, OrderAndContract>();
      ThreadSafeDictionary<string, int> _symbolToUnsubscribeRequestId = new ThreadSafeDictionary<string, int>();
      ThreadSafeDictionary<int, PriceBarsAndIndicatorValues> _requestIdToPriceBarsAndIndicatorValues = new ThreadSafeDictionary<int, PriceBarsAndIndicatorValues>();
      ThreadSafeDictionary<int, PriceBarsAndIndicatorValues> _backfillRequestIdToPriceBarsAndIndicatorValues = new ThreadSafeDictionary<int, PriceBarsAndIndicatorValues>();
      ThreadSafeDictionary<string, float> _accountFieldToAccountValue = new ThreadSafeDictionary<string, float>();

      ListViewObservableThreadSafeHashSet<string> _symbolsWithOpenDaytradeOrders;
      AutoResetEvent _backfillCompleted = new AutoResetEvent(false);

      readonly Func<PriceBarsAndIndicatorValues, StrategyResult>[] _tradingStrategies =
         new Func<PriceBarsAndIndicatorValues, StrategyResult>[]
         {
            TradingStrategy.TestStrategy
            // TradingStrategy.TestStrategy2
            // TradingStrategy.MainStrategy
         };

      readonly Contract[] _presetStocks = new Contract[]
         {
            new Stock("MSFT"),
            new Stock("QQQQ"),
         };

      // Update these ES and YM expiries every three months.
      // There are 3 spaces in the YM symbol.
      public const string YMSymbol = "YM   JUN 09";
      public const string YMExpirationDate = "200906";
      public const string ESSymbol = "ESM9";
      public const string ESExpirationDate = "200906";

      public readonly Future YM = new Future("YM", "ECBOT", YMSymbol, YMExpirationDate);
      public readonly Forex EurUsd = new Forex("EUR", "USD");

      readonly Contract[] _presetFutures = new Contract[]
         {
            new Future("ES", "GLOBEX", ESSymbol, ESExpirationDate),
            new Future("YM", "ECBOT", YMSymbol, YMExpirationDate)
         };

      readonly Contract[] _presetCurrencies = new Contract[]
         {
            new Forex("EUR", "USD"),
            //new Forex("EUR", "GBP")
         };

      readonly Indicator[] _defaultIndicators = new Indicator[]
         {
            new RateOfChange(1, PriceBarType.Close, "oneROC"),
            new RateOfChange(3, PriceBarType.Close, "threeROC"),
            new ModifiedRateOfChange(1, PriceBarType.Close, "roc1i"),
            new ModifiedRateOfChange(2, PriceBarType.Close, "roc2i"),
            new SimpleMovingAverage(10, PriceBarType.Close, "sma10"),
            new SimpleMovingAverage(20, PriceBarType.Close, "sma20"),
            new SimpleMovingAverage(50, PriceBarType.Close, "sma50"),
            new SimpleMovingAverage(200, PriceBarType.Close, "sma200"),
            new LowestLow(0, 9, "lowestLow0to9"),
            new LowestLow(0, 19, "lowestLow0to19"),
            new LowestLow(0, 29, "lowestLow0to29"),
            new HighestHigh(0, 9, "highestHigh0to9"),
            new HighestHigh(0, 19, "highestHigh0to19"),
            new HighestHigh(0, 29, "highestHigh0to29"),
            new HighLowCloseDividedBy3(),
            new Stochastic()
         };

      private void DayTradeMainForm_Load(object sender, EventArgs e)
      {
         ThreadPool.UnsafeQueueUserWorkItem(delegate
         {
            Thread.CurrentThread.Name = "DayTradeMainForm Load Thread";
            bool didSuccessfullyConnect = IBClientWrapper.ConnectToTws();
            if (didSuccessfullyConnect)
            {
               ibClient.RequestOpenOrders();
               ibClient.RequestAccountUpdates(true, "");
            }
         }, null);
      }

      /// <summary>
      /// Subscribes to real time bars for symbols in textBoxSymbols.
      /// </summary>
      private void buttonSubscribe_Click(object sender, EventArgs e)
      {
         if (textBoxSymbols.Text.Length == 0 ||
             textBoxSymbols.Text.Trim() == "") return;
         if (!Connected)
         {
            textBoxMessages.WriteLine("Error: Not connected.");
            return;
         }

         ThreadPool.UnsafeQueueUserWorkItem((object textBoxSymbolsObject) =>
         {
            Thread.CurrentThread.Name = "Subscribe Thread";

            string unsplitSymbols = textBoxSymbolsObject as string;
            string[] symbols = unsplitSymbols.Split(new string[] { "\r\n" },
               StringSplitOptions.RemoveEmptyEntries);
            IEnumerable<Contract> contracts = MakeContractsFromSymbols(symbols);
            IBClientWrapper.SubscribeToRealTimeBars(contracts, NumberOfPriceBarsToStore, 
               RealTimeBarSeconds.Five, TradingHours.AllHours);

         }, textBoxSymbols.Text);

         textBoxSymbols.Clear();
      }

      private void buttonSubscribeFutures_Click(object sender, EventArgs e)
      {
         if (!Connected) return;
         IBClientWrapper.SubscribeToRealTimeBars(_presetFutures,
            NumberOfPriceBarsToStore, _RealTimeBarSeconds, TradingHours.AllHours);
      }
      private void buttonSubscribeCurrencies_Click(object sender, EventArgs e)
      {
         if (!Connected) return;
         IBClientWrapper.SubscribeToRealTimeBars(_presetCurrencies,
            NumberOfPriceBarsToStore, _RealTimeBarSeconds, TradingHours.AllHours);
      }
      private void buttonSubscribeStocks_Click(object sender, EventArgs e)
      {
         if (!Connected) return;
         IBClientWrapper.SubscribeToRealTimeBars(_presetStocks,
            NumberOfPriceBarsToStore, _RealTimeBarSeconds, TradingHours.AllHours);
      }

      /// <summary>
      /// Unsubscribes from real time bars for 
      /// user-selected symbols in listViewRealTimeBarSubscriptions.
      /// </summary>
      private void buttonUnsubscribe_Click(object sender, EventArgs e)
      {
         if (!Connected) return;

         foreach (ListViewItem listViewItem in 
            listViewRealTimeBarSubscriptions.SelectedItems)
         {
            string symbol = listViewItem.Text;
            IBClientWrapper.UnsubscribeFromRealTimeBars(symbol, false);
         }
      }

      /// <summary>
      /// Unsubscribes from all real time bar subscriptions.
      /// </summary>
      private void buttonUnsubscribeAll_Click(object sender, EventArgs e)
      {
         if (!Connected) return;

         ThreadPool.UnsafeQueueUserWorkItem(delegate
         {
            Thread.CurrentThread.Name = "UnsubscribeAll Thread";
            IBClientWrapper.UnsubscribeFromAllRealTimeBars();
         }, null);
      }

      private void buttonDisconnect_Click(object sender, EventArgs e)
      {
         if (Connected)
         {
            ThreadPool.UnsafeQueueUserWorkItem(delegate
            {
               Thread.CurrentThread.Name = "Disconnect Thread";
               IBClientWrapper.DisconnectFromTws();
            }, null);
         }
         else
         {
            textBoxMessages.WriteLine("Already disconnected.");
         }
      }

      private void buttonTest_Click(object sender, EventArgs e)
      {
         if (!Connected) return;

         ThreadPool.UnsafeQueueUserWorkItem(delegate
         {
            Stock contract = new Stock("YHOO");
            IBClientWrapper.SubscribeToRealTimeBars(contract, 5,
               RealTimeBarSeconds.Five, TradingHours.AllHours);
         }, null);
      }

      private void buttonTest2_Click(object sender, EventArgs e)
      {
         if (!Connected) return;

         ThreadPool.UnsafeQueueUserWorkItem(delegate
         {
            LimitOrder order = new LimitOrder(ActionSide.Buy, 1, 13.50m);
            order.TimeInForce = TimeInForce.GoodTillDate;
            order.GoodTillDate = BracketOrderFactory.GetGoodAfterTime(DateTime.Now,
               TimeSpan.FromSeconds(50), DateTime.Today + new TimeSpan(12, 59, 0));

            Stock contract = new Stock("YHOO");
            IBClientWrapper.SubmitOrder(order, contract);

         }, null);
      }

      private void labelMessages_DoubleClick(object sender, EventArgs e)
      {
         textBoxMessages.Clear();
      }
      private void labelTesting1_DoubleClick(object sender, EventArgs e)
      {
         textBoxTesting1.Clear();
      }
      private void labelTesting2_DoubleClick(object sender, EventArgs e)
      {
         textBoxTesting2.Clear();
      }
   }
}
